//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2003-2007
// ALAN and ALAN Map 500 are registered trademarks of ALAN Group.
//
// ALAN Map 500/600 TRL track parser.
//

# include "StdAfx.h"
# include "AlanTrl.h"
# include "Map.h"
# include "Types.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"
# include "ParseHelpers.h"
# include "Wrappers.h"
# include "Globals.h"

class CAlanTrlLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

DECLARE_WPTRK_LOADER (CAlanTrlLoader, "alan-trl", "trl", "ALAN Map 500/600 tracks (*.trl)", ftiTracks);

bool CAlanTrlLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (_cFileSize < 0x40000 + sizeof (CTrlHdr))
		return false;

	const DWORD dwSignature = reinterpret_cast<const CTrlHdr *> (_pData + 0x40000)->dwSignature;
	if (dwSignature == c_dwTrlSignature || dwSignature == c_dwTrl3FlSignature)
		return true;

	return false;
}

LoadStatus_t CAlanTrlLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd  = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	const CTrlHdr * const pHdr = reinterpret_cast<const CTrlHdr *> (_pData + 0x40000);
	const bool bVer20x = pHdr->dwSignature == c_dwTrlSignature;

	const CTrlTrackLog * const pLogs [c_cTrlMaxTracks] = {
		reinterpret_cast<const CTrlTrackLog *> (_pData),
		reinterpret_cast<const CTrlTrackLog *> (_pData + sizeof (CTrlTrackLog)),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x10000),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x10000 + sizeof (CTrlTrackLog)),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x20000),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x20000 + sizeof (CTrlTrackLog)),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x30000),
		reinterpret_cast<const CTrlTrackLog *> (_pData + 0x30000 + sizeof (CTrlTrackLog)),
	};

	// TODO: research.
	const int nDay    =   pHdr->dwDate        & 0xFF;
	const int nMonth  = ((pHdr->dwDate >> 8)  & 0xF);
	const int nYear   = ((pHdr->dwDate >> 12) & 0xF) + 1990;
	const int nSecond =  pHdr->dwTime        & 0x3F;
	const int nMinute = (pHdr->dwTime >> 8)  & 0x3F;
	const int nHour   = (pHdr->dwTime >> 14) & 0x1F;
//	ReportText ("* Date: %02d-%02d-%04d", nDay, nMonth, nYear);
//	ReportText ("* Time: %02d:%02d:%02d", nHour, nMinute, nSecond);

	for (size_t cTrack = 0; /*cTrack < pHdr->wTracks &&*/ cTrack < c_cTrlMaxTracks; ++ cTrack) {
		const CTrlTrack & track = pHdr->tracks [cTrack];
		if (track.dwOccupied == 0)
			continue;
		if (track.wPoints > c_cTrlMaxPoints)
			continue;

		const CTrlTrackLog & log = * (pLogs [cTrack]);

		//
		// Create track.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPolyline);

		rgn.SetTrackType ();

		rgn.pFileRef  = & file_refs.back ();
		rgn.cBeginPos = reinterpret_cast<const char *> (& log) - _pData;
		rgn.cEndPos   = pEnd - _pData;
		rgn.cFileLine = -1;

		rgn.strLabel.assign   (track.strName);
		rgn.strComment.assign (track.strComment, GetLineLen (track.strComment, track.strComment + sizeof (track.strComment) - 1));

		rgn.elements.push_back (rgn_element_t ());
		rgn_element_t & element = rgn.elements.back ();
		element.cLevel      = 0;
		element.cFileOffset = rgn.cBeginPos;

		// Extra.
		track_info_t * const pExtra = rgn.pExtra->wtr.CreateTrack ();

		if (track.dwShow == 0)
			pExtra->wFlags |= track_info_t::defHideInGPS;

		float fAltitudeFactor = 1;
		float fSpeedFactor    = 1;
		if (bVer20x) {
			// Determine OS version by average altitude.
			double dblAltitudeMax = 0;
			for (size_t cPoint = 1; cPoint < track.wPoints && cPoint < c_cTrlMaxPoints; ++ cPoint) {
				const CTrlExtra & extra = log.extra [cPoint];
				if (dblAltitudeMax < extra.wAltitude)
					dblAltitudeMax = extra.wAltitude;
			}
			const float dblAltitudeMax_OS203 = dblAltitudeMax*2;
			const bool bOS204 = dblAltitudeMax_OS203 > 2000;

			// OS 2.0? - 10
			// OS 2.03 - 2
			// OS 2.04 - 0.02
			// OS 2.05 - 0.2
			fAltitudeFactor = bOS204 ? 0.02f : 2;

			// OS 2.0? - 0.1
			// OS 2.03 - 5
			// OS 2.04 - 0.005
			// OS 2.05 - ? 0.05
			fSpeedFactor = bOS204 ? 0.005f : 5;
		} else {
			// NOTE: the specification states 2
			fAltitudeFactor = 0.02f;

			// NOTE: the specification states 0.05
			fSpeedFactor    = 0.005f;
		}

		points_t & points = element.points;
		points.resize (track.wPoints);
		element.pExtra->TrackPoints.resize (track.wPoints);
		for (size_t cPoint = 0; cPoint < track.wPoints && cPoint < c_cTrlMaxPoints; ++ cPoint) {
			const CTrlPoint & pt = log.points [cPoint];
			point_t & point = points [cPoint];

			// Position.
			point.x =  pt.lX/36000.f;
			point.y = -pt.lY/36000.f;

			const CTrlExtra & extra = log.extra [cPoint];

			if (cPoint > 0) {
				// NOTE: the 0-th point contains incorrect altitude and speed.
				trackpoint_info_t & tpi = element.pExtra->TrackPoints [cPoint];

				// Altitude.
				tpi.wFlags |= trackpoint_info_t::defAltitude;
				tpi.fAltitude = extra.wAltitude*fAltitudeFactor;

				// Speed.
				tpi.wFlags |= trackpoint_info_t::defSpeed;
				tpi.fSpeed = extra.wSpeed*fSpeedFactor;
			}
		}

		SetProgressStatus (50*cTrack/c_cTrlMaxTracks);
	}

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;
	_pMap->cHeaderLen = 0;

	SetProgressStatus (50);

	return lsOK;
}
